/**
 * 
 */
package org.dragonfire.util.token;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;

import org.dragonfire.file.BufferedFile;
import org.dragonfire.util.DString;

/**
 * @author Darth Android
 * @date Oct 5, 2007
 */
public class TokenTable {

	public static void main(String[] args) {
		TokenTable tt = new TokenTable("<tokens/ti83p.tok>");
		ArrayList<Token> t = tt.findMatches(new Byte[] {
			(byte) 0xBB
		});
		System.out.println(t);
	}

	private HashSet<Token> tokens = new HashSet<Token>();
	private int longestByteToken = 0;
	private int longestStringToken = 0;

	public TokenTable(String file) {
		loadTokens(BufferedFile.openFile(file));
	}

	public TokenTable() {
	}

	public void addToken(Token t) {
		tokens.add(t);
		longestByteToken = Math.max(t.getBytes().length, longestByteToken);
		longestStringToken = Math.max(longestStringToken, t.getEditText().length());
	}

	public void loadTokens(InputStream file) {
		loadTokens(file, "UTF-8");
	}

	public void loadTokens(InputStream file, String encoding) {
		Scanner in = new Scanner(file, encoding);
		while (in.hasNextLine()) {
			String line = in.nextLine();
			try {
				int equ = line.indexOf('=');
				String bytes = line.substring(0, equ);
				String[] parts = DString.splitTopLevel(line.substring(equ + 1, line.length()), ',');
				Byte[] tokenBytes = new Byte[bytes.length() / 2];
				for (int i = 0; i < bytes.length(); i += 2) {
					tokenBytes[i / 2] = (byte) Integer.parseInt(bytes.substring(i, i + 2), 16);
				}
				String displayText = DString.extractString(parts[0]);
				String editText = displayText;
				if (parts.length > 1)
					editText = DString.extractString(parts[1]);
				addToken(new Token(tokenBytes, displayText, editText, displayText));
			} catch (Exception e) {
				System.err.println("Error loading token: " + line);
			}
		}
	}

	public Token findToken(int key) {
		return findToken(new Byte[] {
			(byte) key
		});
	}

	public Token findToken(Byte[] key) {
		for (Token t : tokens)
			if (Arrays.equals(t.getBytes(), key))
				return t;
		return null;
	}

	public Token findToken(String key) {
		for (Token t : tokens)
			if (t.getEditText().equals(key))
				return t;
		return null;
	}

	public ArrayList<Token> findMatches(String editText) {
		ArrayList<Token> toks = new ArrayList<Token>();
		for (Token t : tokens) {
			if (t.getEditText().length() >= editText.length()
					&& editText.equals(t.getEditText().substring(0, editText.length())))
				toks.add(t);
		}
		return toks;
	}

	public ArrayList<Token> findMatches(Byte[] bytes) {
		ArrayList<Token> toks = new ArrayList<Token>();
		for (Token t : tokens) {
			if (t.getBytes().length < bytes.length)
				continue;
			Byte[] tmp = new Byte[bytes.length];
			System.arraycopy(t.getBytes(), 0, tmp, 0, tmp.length);
			if (t.getBytes().length >= bytes.length && Arrays.equals(bytes, tmp))
				toks.add(t);
		}
		return toks;
	}

	public Token readToken(Byte[] buffer, int idx) {
		for (int i = Math.min(longestByteToken, buffer.length - idx); i > 0; i--) {
			Byte[] subAry = new Byte[i];
			System.arraycopy(buffer, idx, subAry, 0, i);
			Token t = findToken(subAry);
			if (t != null)
				return t;
		}
		int first = buffer[idx];
		return new Token(new Byte[] {
			(byte) first
		}, "Undefined", "{" + Integer.toHexString(first & 0xFF) + "}", "{" + Integer.toHexString(first & 0xFF) + "}");
	}

	public Token readToken(String buffer, int idx) {
		for (int i = Math.min(longestStringToken, buffer.length() - idx); i > 0; i--) {
			Token t = findToken(buffer.substring(idx, idx + i));
			if (t != null)
				return t;
		}
		return new Token(new Byte[] {
			-1
		}, "Undefined", "{" + buffer.charAt(0) + "}", "{" + buffer.charAt(0) + "}");
	}

	public Token[] readTokens(Byte[] buffer) {
		ArrayList<Token> toks = new ArrayList<Token>();
		int idx = 0;
		while (idx < buffer.length) {
			Token t = readToken(buffer, idx);
			idx += t.getSize();
			toks.add(t);
		}
		return toks.toArray(new Token[toks.size()]);
	}

	public Token[] readTokens(String buffer) {
		ArrayList<Token> toks = new ArrayList<Token>();
		int idx = 0;
		while (idx < buffer.length()) {
			Token t = readToken(buffer, idx);
			idx += t.getEditText().length();
			toks.add(t);
		}
		return toks.toArray(new Token[toks.size()]);
	}
}
